import configparser
import os
import re
import shutil

import markdown
from flask import Flask, send_from_directory, send_file
from jinja2 import Environment, FileSystemLoader

# 创建 flask_app
app = Flask(__name__)

# 读取配置文件
config = configparser.ConfigParser()
config.read('config.cfg', encoding='utf-8')


def set_dirs(project):
    global content_dir, template_dir, output_dir, temp_dir, pass_files, plugins, website_title, darkmode_file  # 声明为全局变量

    # 获取项目对应的配置文件路径
    website_cfg_path = os.path.join('app', project, 'website.cfg')

    if os.path.exists(website_cfg_path):
        website_config = configparser.ConfigParser()
        website_config.read(website_cfg_path, encoding='utf-8')

        # 网站标题
        website_title = website_config.get('directories', 'website_title', fallback='')
        # print(website_title)

        # 禁止输出的文件
        pass_files = website_config.get('directories', 'pass_files')

        # 插件
        plugins = website_config.get('directories', 'plugins')

        # 夜间模式插件
        if 'darkmode' in plugins:
            darkmode_file = website_config.get('directories', 'darkmode_file')

        # 目录位置
        content_dir = os.path.abspath(
            os.path.join('app', project, website_config.get('directories', 'content_dir').replace('\\', '/')))
        output_dir = os.path.abspath(
            os.path.join('app', project, website_config.get('directories', 'output_dir').replace('\\', '/')))
        template_dir = os.path.abspath(
            os.path.join('app', project, website_config.get('directories', 'template_dir').replace('\\', '/')))
        temp_dir = os.path.abspath(
            os.path.join('app', project, website_config.get('directories', 'temp_dir').replace('\\', '/')))


        # 如果目录不存在，则创建
        os.makedirs(content_dir, exist_ok=True)
        os.makedirs(output_dir, exist_ok=True)
        os.makedirs(template_dir, exist_ok=True)
        os.makedirs(temp_dir, exist_ok=True)

        app.config['content_dir'] = os.path.abspath(content_dir)
        app.config['output_dir'] = os.path.abspath(output_dir)
        app.config['template_dir'] = os.path.abspath(template_dir)
        app.config['temp_dir'] = os.path.abspath(temp_dir)


# 获取当前激活的项目，默认为 default
project = config.get('directories', 'project', fallback='default')
set_dirs(project)

# 设置静态文件目录
app.static_folder = os.path.join(app.config['output_dir'], 'static')

# print(f'当前项目：{website_title}')

# from plugins.time_plugin import get_current_time  # 获取各种时间写入模版中
#
# # 获得当前时间
# current_time_year = get_current_time('year')
# # print('当前时间', current_time_year)

# markdown2html 主方法
env = Environment(loader=FileSystemLoader(temp_dir))  # 定义 jinja2 在缓存目录下工作


def process_md_files(content_dir, template_dir, output_dir, temp_dir):
    """
    用模版文件比对 md 文件，当文件名一致时，通过 ma2html 进行替换，替换规则是 md 文件里
    <!-- 描述 --><!-- 描述 end -->的部分被替换到 html 文件里 {{ 描述 }} 的对应部分
    然后把文件保存在缓存目录下
    当文件名不一致时，可能是 include 文件，直接复制到缓存目录
    最后用 jinja2 全部渲染，输出到 out_dir
    """
    # 遍历所有 md 文件，打开对应的模版文件，把内容更新到缓存目录对应的 html
    for html_file in os.listdir(template_dir):
        if html_file.endswith('.html'):
            base_name = os.path.splitext(html_file)[0]
            template_page = os.path.join(template_dir, f'{base_name}.html')
            md_page = os.path.join(content_dir, f'{base_name}.md')
            temp_dir_page = os.path.join(temp_dir, f'{base_name}.html')

            # 检查 template_dir 目录里的 html 模板文件是否有对应的 md 文件
            if os.path.exists(md_page):
                # 读取 md 文件内容
                with open(md_page, 'r', encoding='utf-8') as f:
                    md_content = f.read()

                # 提取描述标记之间的内容
                matches = re.findall(r'<!--\s*(.*?)\s*-->\s*(.*?)\s*(?=<!--|$)', md_content, re.DOTALL)

                # 读取模板文件内容
                with open(template_page, 'r', encoding='utf-8') as f:
                    # print(f'打开模版', template_page)
                    template_content = f.read()

                # 替换模板文件中的 {描述} 为提取的内容
                for match in matches:
                    name = match[0].strip()
                    content = match[1].strip()

                    # 将描述内容转换为 HTML
                    html_content = markdown.markdown(content)

                    # 替换模板文件中的标记
                    template_content = template_content.replace(f'{{{{ {name} }}}}', html_content)

                # 替换模板标记
                template_content = template_content.replace('{{ website_title }}', website_title)

                # 写入到 HTML 文件

                with open(temp_dir_page, 'w', encoding='utf-8') as f:
                    # compressed_html = compress_html(template_content)
                    f.write(template_content)

                # print(f'已输出临时文件 {temp_dir_page}')
            else:
                # print(f'未找到对应的md文件：{template_page}，可能是 include 文件')

                # 读取模板文件内容
                with open(template_page, 'r', encoding='utf-8') as f:
                    # print(f'打开模版', template_page)
                    template_content = f.read()

                    # 替换模板标记
                    template_content = template_content.replace('{{ website_title }}', website_title)

                    # 写入到 HTML 文件
                    with open(temp_dir_page, 'w', encoding='utf-8') as f:
                        # compressed_html = compress_html(template_content)
                        f.write(template_content)

                    # print(f'已输出临时文件 {temp_dir_page}')

    # 夜间模式插件
    if 'darkmode' in plugins:
        from plugins.darkmode.darkmode import add_darkmode
        add_darkmode(temp_dir, output_dir, html_file=darkmode_file)  # 配置

    """
    遍历 temp_dir 下所有扩展名为 html 的文件，在 content_dir 目录下判断该 md 文件是否存在
    如果存在，且不在 pass 中，才渲染生成新的 HTML 文件，这样能避免生成多余的 html
    """
    for file_name in os.listdir(temp_dir):
        if os.path.splitext(file_name)[1] == '.html':  # 只处理html
            md_file = os.path.splitext(file_name)[0] + '.md'
            if os.path.exists(os.path.join(content_dir, md_file)):
                if os.path.splitext(file_name)[0] not in pass_files:
                    # print(os.path.join(temp_dir, file_name))

                    if file_name.endswith('.html'):
                        # 读取模板文件内容
                        with open(os.path.join(temp_dir, file_name), encoding='utf-8') as f:
                            template_content = f.read()
                            # print(template_content)

                        # 渲染模板文件
                        template = env.from_string(template_content)
                        rendered_html = template.render()

                        # 写入到输出目录下的 HTML 文件
                        output_file = os.path.join(output_dir, file_name)
                        with open(output_file, 'w', encoding='utf-8') as f:
                            # compressed_html = compress_html(rendered_html)
                            f.write(rendered_html)
                            # f.write(rendered_html)

    """
    通过递归遍历 content_dir 和 template_dir 目录中的文件和文件夹，如果是文件夹，则在目标目录下创建对应的文件夹；
    如果是文件且不是以 .md 结尾，则将文件拷贝到目标目录下。
    拷贝文件时，如果目标目录下已经存在同名文件，则通过比较文件体积和 MD5 值来判断是否覆盖。
    如果体积和 MD5 值都相同，则忽略，否则直接覆盖。
    """
    # 递归拷贝函数
    def copy_files(source, destination):
        if os.path.isdir(source):  # 如果是文件夹
            if not os.path.exists(destination):  # 如果目标文件夹不存在，则创建
                os.makedirs(destination)
            for item in os.listdir(source):  # 遍历文件夹中的所有项目
                source_item = os.path.join(source, item)
                destination_item = os.path.join(destination, item)
                copy_files(source_item, destination_item)  # 递归调用拷贝函数
        else:  # 如果是文件
            if not source.endswith('.md') and not source.endswith('.html'):  # 排除
                shutil.copy2(source, destination)  # 使用 shutil 模块的 copy2 函数进行拷贝

    copy_files(content_dir, output_dir)  # 拷贝 content_dir 目录
    copy_files(template_dir, output_dir)  # 拷贝 template_dir 目录


# 渲染 html 动作
def execute_actions():
    # 调用 markdown2html 主方法
    process_md_files(content_dir, template_dir, output_dir, temp_dir)

    # 压缩
    if 'compressor' in plugins:
        from plugins.compressor import compress_files  # 压缩html js css
        compress_files(output_dir)


# 看门🐕
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer


# 文件变化监听器
class FileChangeHandler(FileSystemEventHandler):
    def __init__(self):
        self.file_state = {}

    def on_any_event(self, event):
        if event.is_directory:
            return

        file_path = event.src_path
        if file_path not in self.file_state:
            self.file_state[file_path] = ''

        current_state = self.get_file_state(file_path)
        if current_state is None:
            if event.event_type == 'deleted':
                print(f"File {file_path} has been deleted.")
                del self.file_state[file_path]
            return

        if self.file_state[file_path] != current_state:
            if event.event_type == 'created':
                print(f"File {file_path} has been created. Current state: {current_state}")
            elif event.event_type == 'modified':
                print(f"File {file_path} has been modified. Current state: {current_state}")
            elif event.event_type == 'deleted':
                print(f"File {file_path} has been deleted.")
            self.file_state[file_path] = current_state

    def on_moved(self, event):
        from_path = event.src_path
        to_path = event.dest_path
        if from_path in self.file_state:
            print(f"File {from_path} has been renamed to {to_path}")
            current_state = self.get_file_state(to_path)
            self.file_state[to_path] = current_state
            del self.file_state[from_path]

    def get_file_state(self, file_path):
        if not os.path.exists(file_path):
            return None

        file_size = os.path.getsize(file_path)
        file_modified_time = os.path.getmtime(file_path)
        return f"Size: {file_size}, Modified Time: {file_modified_time}"


def on_event_trigger(event):
    print('* Detected file changes...')
    execute_actions()


# 创建文件变化监听器
event_handler = FileChangeHandler()
observer = Observer()
observer.schedule(event_handler, app.config['content_dir'], recursive=True)
observer.schedule(event_handler, app.config['template_dir'], recursive=True)
observer.start()

event_handler.on_any_event = on_event_trigger


# 路由生成器
def generate_routes():
    html_files = [f for f in os.listdir(output_dir) if f.endswith('.html')]
    dir_routes = []
    for dirpath, dirnames, filenames in os.walk(output_dir):

        # 遍历 output_dir 目录下所有子目录，生成对应的路由
        for dirname in dirnames:
            route_path = '/' + dirpath.replace(output_dir, '', 1).lstrip('/') + '/' + dirname
            dir_routes.append(route_path)

    html_routes = ['/' + os.path.splitext(file_name)[0] for file_name in html_files]

    return html_routes + dir_routes


@app.route('/')
def index():
    return send_file(os.path.join(output_dir, 'index.html'))


@app.route('/static/<path:filename>')
def static_files(filename):
    return send_from_directory(app.static_folder, filename)


@app.route('/<path:path>')
def serve_html(path):
    return send_from_directory(output_dir, path)


def start_app():
    routes = generate_routes()
    for route in routes:
        app.add_url_rule(route, endpoint=route, view_func=serve_html)
    execute_actions()
    app.run(debug=True)


if __name__ == "__main__":
    start_app()
